package io.renren.wechat.controller;

import io.renren.wechat.common.aes.AesException;
import io.renren.wechat.common.aes.WXBizCmdCrypt;
import io.renren.wechat.common.aes.WXBizCorpCrypt;
import io.renren.wechat.common.aes.WXBizMsgCrypt;
import io.renren.wechat.config.WechatProperties;
import io.renren.wechat.service.office.OfficeService;
import io.renren.wechat.service.suite.SuiteService;
import io.renren.wechat.service.suite.SuiteTicketService;
import io.renren.wechat.service.suite.dto.PermanentAuthInfoResponse;
import io.renren.wechat.support.WXPayUtil;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.nio.charset.StandardCharsets;
import java.util.Map;


/**
 * 指令和数据回调URL
 */
@Controller
@RequestMapping("/wechat/suite")
public class WechatSuiteEventController {
    private static final Logger LOGGER = LoggerFactory.getLogger(WechatSuiteEventController.class);
    private WXBizCorpCrypt corpCrypt;
    private WXBizMsgCrypt dataCrypt;
    private WXBizCmdCrypt cmdCrypt;
    private SuiteTicketService suiteTicketService;
    private WechatProperties wechatProperties;
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    private SuiteService suiteService;
    private OfficeService officeService;

    @GetMapping("/data")
    public void data(@RequestParam("msg_signature") String msgSignature,
                     @RequestParam("timestamp") String timestamp,
                     @RequestParam("nonce") String nonce,
                     @RequestParam("echostr") String echoStr,
                     HttpServletResponse response) throws Exception {
        LOGGER.debug("post recvDataNotify parameters: msgSignature=[{}], timestamp=[{}], nonce=[{}], echostr=[{}]",
                msgSignature, timestamp, nonce, echoStr);

        try {
            String plainMsgText = null;
            try {
                plainMsgText = cmdCrypt.VerifyURL(msgSignature, timestamp, nonce, echoStr);
            } catch (AesException e) {
                e.printStackTrace();
                WXBizCmdCrypt cmdCrypt2 = new WXBizCmdCrypt(wechatProperties.getSuiteToken2(),
                        wechatProperties.getSuiteEncodingAESKey2(),
                        wechatProperties.getCorpId());
                plainMsgText = cmdCrypt2.VerifyURL(msgSignature, timestamp, nonce, echoStr);

            }
            LOGGER.info("recvMsgNotify plainMsgText: [{}]", plainMsgText);

            response.getWriter().print(plainMsgText);
        } catch (Exception e) {
            LOGGER.error("recvMsgNotify failed", e);
            response.setStatus(HttpStatus.BAD_REQUEST.value());
        }
    }

    @PostMapping("/data")
    public void data(@RequestParam("msg_signature") String msgSignature,
                     @RequestParam("timestamp") String timestamp,
                     @RequestParam("nonce") String nonce,
                     HttpServletRequest request,
                     HttpServletResponse response) throws Exception {
        try (final ServletInputStream inputStream = request.getInputStream()) {
            LOGGER.debug("post recvDataNotify parameters: msgSignature=[{}], timestamp=[{}], nonce=[{}]",
                    msgSignature, timestamp, nonce);

            final String requestBody = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
            LOGGER.debug("request body: {}", requestBody);
            if (StringUtils.isBlank(requestBody)) {
                response.getWriter().print("success");
                return;
            }

            final Map<String, String> xmlToMap = WXPayUtil.xmlToMap(requestBody);
            final String toUserName = xmlToMap.get("ToUserName");
            LOGGER.info("posting_data to [{}]", toUserName);

            String suiteToken = wechatProperties.getSuiteToken();

            WXBizMsgCrypt msgTools = new WXBizMsgCrypt(suiteToken, wechatProperties.getSuiteEncodingAESKey(),
                    toUserName);

            String xmlMsg = null;
            try {
                xmlMsg = msgTools.DecryptMsg(msgSignature, timestamp, nonce, requestBody);
            } catch (AesException e) {
                e.printStackTrace();

                //第二个应用
                suiteToken = wechatProperties.getSuiteToken2();

                msgTools = new WXBizMsgCrypt(suiteToken, wechatProperties.getSuiteEncodingAESKey2(),
                        toUserName);
                xmlMsg = msgTools.DecryptMsg(msgSignature, timestamp, nonce, requestBody);
            }

            LOGGER.debug("decrypt request msg: {}", xmlMsg);

            final Map<String, String> dataMap = WXPayUtil.xmlToMap(xmlMsg);
            final String infoType = dataMap.get("InfoType");
            LOGGER.debug("recv data infoType: {}", infoType);

            /* 推送suite_ticket */
            final String suiteId = dataMap.get("SuiteId");
            if (StringUtils.equalsIgnoreCase(infoType, "suite_ticket")) { // 推送suite_ticket
                suiteTicketService.cacheSuiteTicket(suiteId, dataMap.get("SuiteTicket"));
            } else if (StringUtils.equalsIgnoreCase(infoType, "create_auth")) { // 授权成功通知
                threadPoolTaskExecutor.submit(() -> {
                    final String authCode = dataMap.get("AuthCode");
                    suiteTicketService.cacheAuthCode(suiteId, authCode);

                    PermanentAuthInfoResponse corpInfo = suiteService.onCreateAuth(suiteId, toUserName, authCode);

                    try {
                        officeService.syncCorp(corpInfo.getAuthCorpInfo().getCorpId(), corpInfo.getAuthCorpInfo().getCorpName(), corpInfo.getAuthInfo().getAgentList());
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                });
            } else if (StringUtils.equalsIgnoreCase(infoType, "change_auth")) {
                final String authCorpId = dataMap.get("AuthCorpId");


            } else if (StringUtils.equalsIgnoreCase(infoType, "cancel_auth")) {
                threadPoolTaskExecutor.submit(() -> {
                    final String authCorpId = dataMap.get("AuthCorpId");
                    suiteService.onCancelAuth(suiteId, authCorpId);
                });
            } else if (StringUtils.equalsIgnoreCase(infoType, "change_contact")) {
                /* changeType=[create_user, update_user, delete_user, update_tag, delete_party, update_party, create_party] */
                final String changeType = dataMap.get("ChangeType");
            }

            final String msgtype = dataMap.get("MsgType");
            if (StringUtils.equalsIgnoreCase(msgtype, "Event")) {
                final String event = dataMap.get("Event");
                LOGGER.debug("recv event: {}", event);

                switch (event) {
                    case "enter_agent":
                        String finalSuiteToken = suiteToken;
                        threadPoolTaskExecutor.submit(() -> {
                            final String corpId = dataMap.get("ToUserName");
                            final String visitorUserId = dataMap.get("FromUserName");
                            final String agentId = dataMap.get("AgentID");

                            LOGGER.debug("userId: {} in corpId: {} enter agentId: {}", visitorUserId, corpId, agentId);

                            suiteService.cacheVisitingAgentId(corpId, agentId, visitorUserId, finalSuiteToken);
                        });

                        break;
                    case "change_app_admin":

                        break;
                    default:
                        break;
                }

            }

            response.getWriter().print("success");
        }
    }

    private boolean isToCorp(String toUserName) {
        return StringUtils.equals(toUserName, wechatProperties.getCorpId());
    }

    private boolean isToSuite(String toUserName) {
        return StringUtils.equals(toUserName, wechatProperties.getSuiteId());
    }

    @RequestMapping("/cmd")
    public void cmd(@RequestParam("msg_signature") String msgSignature,
                    @RequestParam("timestamp") String timestamp,
                    @RequestParam("nonce") String nonce,
                    @RequestParam("echostr") String echoStr,
                    HttpServletResponse response) {
        LOGGER.info("get recvMsgNotify parameters: msgSignature=[{}], timestamp=[{}], nonce=[{}], echostr=[{}]",
                msgSignature, timestamp, nonce, echoStr);
        try {
            final String plainMsgText = cmdCrypt.VerifyURL(msgSignature, timestamp, nonce, echoStr);
            LOGGER.info("recvMsgNotify plainMsgText: [{}]", plainMsgText);

            response.getWriter().print(plainMsgText);
        } catch (Exception e) {
            LOGGER.error("recvMsgNotify failed", e);
            response.setStatus(HttpStatus.BAD_REQUEST.value());
        }
    }

    @Autowired
    public void setCmdCrypt(WXBizCmdCrypt cmdCrypt) {
        this.cmdCrypt = cmdCrypt;
    }

    @Autowired
    public void setDataCrypt(WXBizMsgCrypt dataCrypt) {
        this.dataCrypt = dataCrypt;
    }

    @Autowired
    public void setCorpCrypt(WXBizCorpCrypt corpCrypt) {
        this.corpCrypt = corpCrypt;
    }

    @Autowired
    public void setSuiteTicketService(SuiteTicketService suiteTicketService) {
        this.suiteTicketService = suiteTicketService;
    }

    @Autowired
    public void setWechatProperties(WechatProperties wechatProperties) {
        this.wechatProperties = wechatProperties;
    }

    @Autowired
    public void setThreadPoolTaskExecutor(ThreadPoolTaskExecutor threadPoolTaskExecutor) {
        this.threadPoolTaskExecutor = threadPoolTaskExecutor;
    }

    @Autowired
    public void setSuiteService(SuiteService suiteService) {
        this.suiteService = suiteService;
    }

    @Autowired
    public void setOfficeService(OfficeService officeService) {
        this.officeService = officeService;
    }
}
